%% Rutas aéreas predefinidas.

% llega(?origen, ?destino, ?tiempo)
% se aclara a swi que se puede modificar en runtime el predicado llega/3, para poder testear
:- dynamic llega/3.

llega(buenos_aires, mdq, 1).
llega(buenos_aires, cordoba, 2).
llega(buenos_aires, rio, 4).
llega(buenos_aires, madrid, 12).
llega(buenos_aires, atlanta, 11).
llega(mdq, buenos_aires, 1).
llega(cordoba, salta, 1).
llega(salta, buenos_aires, 2).
llega(rio, buenos_aires, 4).
llega(rio, new_york, 10).
llega(atlanta, new_york, 3).
llega(new_york, madrid, 9).
llega(madrid, lisboa, 1).
llega(madrid, buenos_aires, 12).
llega(lisboa, madrid, 1).
llega(new_york, buenos_aires, 11).

%% Aviones y sus autonomías.

% autonomia(?avion, ?horas)
autonomia(boeing_767, 15).
autonomia(airbus_a320, 9).
autonomia(boeing_747, 10).


%% Predicados pedidos:

/*---- Auxiliares ----*/
esCiudad(C):- llega(C, _, _); llega(_, C, _).
esAvion(A):- autonomia(A,_).

/*
 * Ejercicio 1
 */
ciudades(C):- setof(X, esCiudad(X), C).


/*
 * Ejercicio 2
 * viajeDesde(+Origen,?Destino,-Recorrido,-Tiempo) -- Devuelve infinitos resultados
 *
 * Genera los resultados sin irse por una rama.
 */
viajaDesde(Ciudad, Ciudad, [Ciudad], 0).
viajaDesde(Origen, Destino, [Origen|Resto], Tiempo):-
                    viajaDesde(Escala, Destino, Resto, T2),
                    llega(Origen, Escala, T1),
                    Tiempo is T1+T2.


/*
 * Ejercicio 3
 * viajeSinCiclos(+Origen,?Destino,-Recorrido,-Tiempo)
 *
 * Se utiliza la función auxiliar: viajeSinCiclos(+Origen,?Destino,-Recorrido,-Tiempo).
 * Se va "guardando" cuáles son los lugares que ya visitó para no volver a elegirlos como escala.
 */
viajeSinCiclos(Origen, Destino, Recorrido, Tiempo):-
                    vsc(Origen, Destino, [Origen], Recorrido, Tiempo).

vsc(Origen, Origen, _,  [Origen], 0).
vsc(Origen, Destino, RecorridoHecho, [Origen | Resto], Tiempo):-
                    llega(Origen, Escala, T1),
                    not(member(Escala, RecorridoHecho)),
                    vsc(Escala, Destino, [Escala | RecorridoHecho], Resto, T2),
                    Tiempo is T1+T2.

/*
 * Ejercicio 4
 * viajeMasCorto(+Origen,+Destino,-Recorrido,-Tiempo)
 *
 * Idea: El viaje más corto es el viaje sin ciclos tq. todo otro viaje tiene tiempo >= que el suyo.
 */
viajeMasCorto(Ciudad, Ciudad, [Ciudad], 0).
viajeMasCorto(Origen, Destino, Recorrido, Tiempo):-
                      viajeSinCiclos(Origen, Destino, Recorrido, Tiempo),
                      forall(viajeSinCiclos(Origen, Destino, _, T), T >= Tiempo).

/*
 * Ejercicio 5
 * Toma una ciudad como referencia y se fija que alcance y sea alcanzable por todas.
 */
grafoCorrecto:- unaCiudad(Ciudad),                                                 % dada una ciudad de referencia
                ciudades(C),                                                       % y todas las ciudades
                forall(member(OtraCiudad, C), llegaYAlcanza(Ciudad, OtraCiudad)).  % puede alcanzar y ser alcanza por el resto

unaCiudad(Ciudad) :- ciudades([Ciudad|_]).
llegaYAlcanza(Ciudad, OtraCiudad):- viajeSinCiclos(Ciudad, OtraCiudad, _, _), viajeSinCiclos(OtraCiudad, Ciudad, _, _).

/*
 * Ejercicio 6
 * cubreDistancia(+Recorrido, ?Avion)
 * Mientras haya al menos dos ciudades en el recorrido, las toma de pares y se fija si "hay ruta"
 * entre ambas. Si hay, chequea que la ruta entre las rentantes ciudades sea valida.
 */

cubreDistancia([], Avion):- esAvion(Avion).
cubreDistancia([Ciudad], Avion):- esCiudad(Ciudad), esAvion(Avion).
cubreDistancia([Origen, Escala| Resto], Avion):-
              llega(Origen, Escala, T1),
              autonomia(Avion, Autonomia),
              Autonomia >= T1,
              cubreDistancia([Escala|Resto], Avion).

/*
 * Ejercicio 7
 * vueloSinTransbordo(+Aerolinea, +Ciudad1, +Ciudad2, ?Tiempo, ?Avion)
 */
vueloSinTransbordo(Aerolinea, Ciudad1, Ciudad2, Tiempo, Avion):-
                        estadoValido(Aerolinea),                            % las bases operativas están bien construídas
                        avionUbicado(Aerolinea, Ciudad1, Avion),            % el avión está en la base operativa del orígen
                        viajeMasCorto(Ciudad1, Ciudad2, Recorrido, Tiempo), % se conocen el tiempo y recorrido óptimos
                        cubreDistancia(Recorrido, Avion).                   % el avión está en condiciones de realizar el recorrido.

% avionUbicado(+Aerolinea, +Ciudad, +Avion)
% Indica si el avión se encuentra ubicado en la base operativa de dicha ciudad
avionUbicado([(UnaCiudad, Aviones) | Resto], Ciudad, Avion):-
                        Ciudad = UnaCiudad, member(Avion, Aviones); % si es la misma ciudad, me fijo que el avión esté
                        avionUbicado(Resto, Ciudad, Avion).         % si no, paso la siguiente base operativa

% estadoValido(+Aerolinea)
% Chequea las bases operativas de la aerolínea sean correctas.
estadoValido([]).
estadoValido([ (Ciudad,Aviones) | OtrasBasesOperativas ]):-
                        esCiudad(Ciudad),                                                                           % la ciudad existe
                        forall(member(Avion,Aviones), esAvion(Avion)),                                              % los aviones existen
                        forall(member(BaseOperativa, OtrasBasesOperativas), distintaCiudad(BaseOperativa, Ciudad)), % no se repiten ciudades
                        estadoValido(OtrasBasesOperativas), !.                                                      % el resto de las bases operativas también son válidas
distintaCiudad((Ciudad, _), OtraCiudad):- Ciudad \= OtraCiudad.


% Cargamos las pruebas.
?- [pruebas].
